/**
*  @file bq_pack.c 
*
*  @brief this file contains all the definitions of the BQ76PL536 devices.
*
*  @author Daniel T. & Marcin N. Texas Instruments, Inc
*  @date November 2010
*  @version 1.0 Initial version
*  @note Built with IAR for MSP430 Version: 5.10
*/

#include "bq_pack.h"

/******************************************************************************/
/*                            Global variables                                */
/******************************************************************************/
//Global variable that contains the battery pack information
bq_pack_t bq_pack;
//SW flag that controls the Charge/Discharge mode
unsigned char HOST_CONTROL_IN = 0;

/******************************************************************************/
/*                            Local variables                                 */
/******************************************************************************/

//Array that stores a copy of the last known cell voltages

static unsigned short cell_values[NUMBER_OF_CELLS];

/**
* @brief Function Name: bq_pack_init.
* @brief Description  : Configure each BQ device in the stack and initialize
* data structures for whole BQ stack .
* @param parameters   :  none 
* @return Value       :  return  if there isn't problems while init the pack
*/

//#define BROADCAST_ADDR            0x3F
//#define DISCOVERY_ADDR            0x00
//#define BQ76PL536_RESET           0xa5

short bq_pack_address_discovery(void)
{
  unsigned short i, n;
  unsigned char reg_val[2];

  i=NUMBER_OF_BQ_DEVICES; //controls iteration loop

  while (i>0)
  {
    //*Send BROADCAST_RESET to address 0x00*/
    bq_dev_write_reg(BROADCAST_ADDR, RESET_REG, BQ76PL536_RESET);
    
    n=0;  //controls number of discovered devices
	//NUMBER_OF_BQ_DEVICES      1
	 
    while (n < NUMBER_OF_BQ_DEVICES)
    {
	//*Read DEVICE_STATUS reg at address 0x00*/

//redefines the SPI read/write functions
//#define bq_dev_write_reg(a,b,c) spi_write_reg(a,(unsigned char)b,c)
//#define bq_dev_read_reg(a,b,c,d,e) spi_read_reg(a,(unsigned char)b,c,d,e)
//#define DISCARD_CRC               (1)
//#define RETURN_CRC                (0)
//#define NUMBER_OF_BQ_DEVICES      1  //BQ76PL536 devices are connected
//#define NUMBER_OF_CELLS           6 //MAX number of cells in the system
//#define MAX_CELLS_NUMBER_IN_BQ    6  //MAX number of cells per BQ76PL536 device
//#define CELL_BALANCING_EN         1  //set to 1 to enable cell balancing
//#define ONE_MINUTE                60

    bq_dev_read_reg(DISCOVERY_ADDR, DEVICE_STATUS_REG, 1, DISCARD_CRC, reg_val);
  
      //*Verify if MSB is equal to 0*/
    if (reg_val[0] & (1<<7))
    {
      n = NUMBER_OF_BQ_DEVICES; //break internal loop
    }
    else
    {
     //*Assign a new address*/        
     //Save assigned address
      n++;
   	 //bq_dev_t bq_devs[NUMBER_OF_BQ_DEVICES];
      bq_pack.bq_devs[n-1].device_address = n;
        
     //ADDR_CTRL = n;  
     //  ADDRESS_CONTROL_REG=0x3b, 
      bq_dev_write_reg(DISCOVERY_ADDR, ADDRESS_CONTROL_REG, n);

	for(i = 0; i < 0xbffff; i++)	//Delay
	{
		__nop();	// 117ms 
	}

     //read ADDR_CTRL
      bq_dev_read_reg(n, ADDRESS_CONTROL_REG, 1, DISCARD_CRC, reg_val);

      if ((reg_val[0]&0x3F) == n)
      {
        //address next device or finish device detection
         if (n==NUMBER_OF_BQ_DEVICES)
         return n;
      }
      else
      {
          //break internal loop
         n = NUMBER_OF_BQ_DEVICES;
      }
     }
    }
    i--;
  }
  return 0;
}

/**
* @brief Function Name: bq_pack_init.                                              
* @brief Description  : Configure each BQ device in the stack and initialize 
* data structures for whole BQ stack.
* @param parameters   : none                                                  
* @return Value       : return  if there isn't problems while init the pack  
*/

/* To initiallize bq_pack */

short bq_pack_init(void)
{
  unsigned char i;

/* #define NUMBER_OF_BQ_DEVICES      1  //3 BQ76PL536 devices are connected */

  for (i=0; i<NUMBER_OF_BQ_DEVICES; i++)
  {
    //*Init cell count for each BQ device*/ 

	// #define MAX_CELLS_NUMBER_IN_BQ    6  //MAX number of cells per BQ76PL536 device

    bq_pack.bq_devs[i].cell_count = MAX_CELLS_NUMBER_IN_BQ;  

    //*Configure each BQ device*/
    //* bq_pack.bq_devs[i]
    conf_bq_dev(&bq_pack.bq_devs[i]);

    //*Initilize data structures*/    
    init_bq_dev_data_structure(&bq_pack.bq_devs[i]);

    //*Read cell voltage*/
    bq_dev_read_cell_voltage(&bq_pack.bq_devs[i]);
  }
  //battery manager initial mode
  bq_pack.op_mode = FAULT_MODE;
  bq_pack.error_status = 0;
  bq_pack.voltage = 0;
  bq_pack.timer_status = 0;
  bq_pack.eoc_eod_timer = 0;
  bq_pack.charge_taper_timer = 0;
  bq_pack.chg_dschg_op_timer = 0;
  bq_pack.balancing_timer = 0;
  bq_pack.max_balance_timer = 0;
  bq_pack.last_imbalanced_cell_idx = 0;
  bq_pack.cell_imbalance_fail_timer = 0;
	// set the battery manager to initial mode
	// update_op_mode(INITIAL_MODE);
  return 0;
}

short bq_dev_read_cell_voltage(bq_dev_t* this)
{
  unsigned char* pPtr;
  unsigned char temp;
  short i, ret_val;
  unsigned long voltage_comput;
  
  unsigned char ADD=0x01;
  unsigned char V_Number = 0x0C;

 /* 
  ret_val = bq_dev_read_reg(this->device_address, VCELL1_L_REG, 
            (MAX_CELLS_NUMBER_IN_BQ<<1) , DISCARD_CRC, 
            (unsigned char *) &this->cell_voltage[0]);
 */

  ret_val = bq_dev_read_reg(ADD, VCELL1_L_REG, 
            V_Number, DISCARD_CRC, 
            (unsigned char *) &this->cell_voltage[0]);
 
  for (i=0; i<MAX_CELLS_NUMBER_IN_BQ; i++)
  {
    //Swap the data in the array as BQ dev returns data in Big Endian notation
    pPtr = (unsigned char *)(&this->cell_voltage[i]);
    temp = *pPtr;
    *(pPtr) = *(pPtr+1);
    *(pPtr+1) = temp;

    //compute cell voltage -> cell_voltage=VCELL x 6250/16383
    voltage_comput = this->cell_voltage[i] * (unsigned long)(adc_step_mul);
    this->cell_voltage[i] = voltage_comput/((unsigned long)(adc_step_div));
  }
  return ret_val;
}

/**
* @brief Function Name:  conf_bq_dev                  .                              
* @brief Description  :  configures each BQ76PL536 included in the stack.
* @param parameters   :  number of BQ device in the stack                                                   
* @return Value       :  none                                                   
*/

void conf_bq_dev(bq_dev_t* this)
{
  unsigned short temp;
  
  bq_dev_write_reg(this->device_address, ADC_CONTROL_REG, ADC_CONTROL_VAL_6);
  
  bq_dev_write_reg(this->device_address, IO_CONTROL_REG, IO_CONTROL_VAL);

  bq_dev_write_reg(this->device_address, CB_CTRL_REG, CB_CTRL_VAL);

  bq_dev_write_reg(this->device_address, CB_TIME_REG, CB_TIME_VAL);

  bq_dev_write_reg(this->device_address, SHDW_CTRL_REG, SHDW_CTRL_ACCESS_EN_VAL);
  
  bq_dev_write_reg(this->device_address, FUNCTION_CONFIG_REG, FUNC_CONFIG_VAL_6);
  
  bq_dev_write_reg(this->device_address, SHDW_CTRL_REG, SHDW_CTRL_ACCESS_EN_VAL);

  bq_dev_write_reg(this->device_address, IO_CONFIG_REG, IO_CONFIG_VAL);

  temp = get_u32_value(COV_THRESHOLD);

  if (temp > 2000)
    temp = (temp - 2000)/50;
  else
    temp = 0;
  
  bq_dev_write_reg(this->device_address, SHDW_CTRL_REG, SHDW_CTRL_ACCESS_EN_VAL);
  bq_dev_write_reg(this->device_address, CONFIG_COV_REG, CONFIG_COV_VAL | temp);
  bq_dev_write_reg(this->device_address, SHDW_CTRL_REG, SHDW_CTRL_ACCESS_EN_VAL);
  bq_dev_write_reg(this->device_address, CONFIG_COVT_REG, 
                   CONFIG_COVT_VAL | COV_TIME);

  temp = get_u32_value(CUV_THRESHOLD);
  if (temp > 700)
    temp = (temp - 700)/100;
  else
    temp = 13;  /*Def CUV Threshold value = 2000mV*/
  bq_dev_write_reg(this->device_address, SHDW_CTRL_REG, SHDW_CTRL_ACCESS_EN_VAL);
  /*Def CUV Threshold = 2V*/
  bq_dev_write_reg(this->device_address, CONFIG_CUV_REG, CONFIG_CUV_VAL | temp);
  bq_dev_write_reg(this->device_address, SHDW_CTRL_REG, SHDW_CTRL_ACCESS_EN_VAL);
  bq_dev_write_reg(this->device_address, CONFIG_CUVT_REG, CONFIG_CUVT_VAL 
                   | CUV_TIME);

  bq_dev_write_reg(this->device_address, SHDW_CTRL_REG, SHDW_CTRL_ACCESS_EN_VAL);
  /*TS2=TS1=50stC*/
  bq_dev_write_reg(this->device_address, CONFIG_OT_REG, CONFIG_OT_VAL);
  bq_dev_write_reg(this->device_address, SHDW_CTRL_REG, SHDW_CTRL_ACCESS_EN_VAL);
  /*Over Temp Time delay = 2000ms*/
  bq_dev_write_reg(this->device_address, CONFIG_OTT_REG, CONFIG_OTT_VAL);

  return;
}

void init_bq_dev_data_structure(bq_dev_t* this)
{
  bq_dev_read_reg(this->device_address, DEVICE_STATUS_REG, 1, DISCARD_CRC, 
                 (unsigned char*) &this->device_status);
    
  bq_dev_read_reg(this->device_address, TEMPERATURE1_L_REG, 2, DISCARD_CRC, 
                 (unsigned char*) &this->temperature1);
  bq_dev_read_reg(this->device_address, TEMPERATURE2_L_REG, 2, DISCARD_CRC, 
                 (unsigned char*) &this->temperature2);
  /*Errors handling*/
  bq_dev_read_reg(this->device_address, ALERT_STATUS_REG, 1, DISCARD_CRC, 
                 (unsigned char*) &this->alert_status);
  bq_dev_read_reg(this->device_address, FAULT_STATUS_REG, 1, DISCARD_CRC, 
                 (unsigned char*) &this->fault_status);
  bq_dev_read_reg(this->device_address, COV_FAULT_REG, 1, DISCARD_CRC, 
                 (unsigned char*) &this->cov_fault);
  bq_dev_read_reg(this->device_address, CUV_FAULT_REG, 1, DISCARD_CRC, 
                 (unsigned char*) &this->cuv_fault);

  /*Clear alerts and faults (May not be so trivial...)*/
  bq_dev_clear_alerts(this);
  bq_dev_clear_faults(this);  

  return;
}

/**
* @brief Function Name: bq_dev_clear_alerts .                                                 
* @brief Description  : Clear the alert flags on the BQ device.
* @param parameters   : specific device ID                                                    
* @return Value       : none                                                    
*/     

void bq_dev_clear_alerts(bq_dev_t* this)
{
  unsigned char Value;

  //clear alert bit in device status register
  bq_dev_read_reg(this->device_address, DEVICE_STATUS_REG, 1, DISCARD_CRC, 
                 (unsigned char*) &Value);
	//  Value |= BIT5; 
  Value |= 0x20;
  //set alert bit as 1
  bq_dev_write_reg(this->device_address, DEVICE_STATUS_REG, Value);
	//  Value &= ~BIT5; 
  
 Value &=   0xDF;
  //clear alert bit
  bq_dev_write_reg(this->device_address, DEVICE_STATUS_REG, Value);
  
  //Read ALERT_STATUS_REG register
  bq_dev_read_reg(this->device_address, ALERT_STATUS_REG, 1, DISCARD_CRC, 
                 (unsigned char*) &Value); 
  //Write 1's to ALERT_STATUS_REG register
  bq_dev_write_reg(this->device_address, ALERT_STATUS_REG, Value);

  Value = 0x00;
  // Write 0's ALERT_STATUS_REG register
  bq_dev_write_reg(this->device_address, ALERT_STATUS_REG, Value);
}

/**
* @brief Function Name: bq_dev_clear_faults.                                                 
* @brief Description  : Clears the fault flags on the BQ device.
* @param parameters   : Device ID                                                     
* @return Value       : none                                                    
*/

void bq_dev_clear_faults(bq_dev_t* this)
{
  unsigned char Value;
  
    //clear fault bit in device status register
  bq_dev_read_reg(this->device_address, DEVICE_STATUS_REG, 1, DISCARD_CRC, 
                 (unsigned char*) &Value);
	//  Value |= BIT6; 
  Value = 0x40;
	//set fault bit as 1
  bq_dev_write_reg(this->device_address, DEVICE_STATUS_REG, Value);
	//  Value &= ~BIT6; 
  Value &= 0xBF;
	//clear fault bit
  bq_dev_write_reg(this->device_address, DEVICE_STATUS_REG, Value);

	//Read FAULT_STATUS register
  bq_dev_read_reg(this->device_address, FAULT_STATUS_REG, 1, DISCARD_CRC, 
                 (unsigned char*) &Value); 
	//Write 1's to FAULT_STATUS register
  bq_dev_write_reg(this->device_address, FAULT_STATUS_REG, Value);

  Value = 0x00;
	// Write 0's FAULT_STATUS register
  bq_dev_write_reg(this->device_address, FAULT_STATUS_REG, Value);
}
/*EOF*/
